package com.xyazm.order.statemachine.action.aftersale;

import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.enums.RefundStatusEnum;
import com.xyazm.common.utils.ParamCheckUtil;
import com.xyazm.order.domain.dto.AfterSaleStateMachineDTO;
import com.xyazm.order.domain.entity.AfterSaleInfoDO;
import com.xyazm.order.domain.entity.AfterSaleLogDO;
import com.xyazm.order.domain.entity.AfterSaleRefundDO;
import com.xyazm.order.domain.param.PayRefundCallbackParam;
import com.xyazm.order.enums.AfterSaleOperateTypeEnum;
import com.xyazm.order.enums.AfterSaleStateChangeEnum;
import com.xyazm.order.enums.AfterSaleStatusEnum;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.manager.MessageManager;
import com.xyazm.order.statemachine.action.AfterSaleStateAction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Date;

/**
 * 售后支付退款回调 action
 * @author xuyou
 * @date 2023年02月26日 21:37
 */
@Slf4j
@Component
public class RefundPayCallbackAction extends AfterSaleStateAction<AfterSaleStateMachineDTO> {

    @Autowired
    private MessageManager messageManager;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public AfterSaleStateChangeEnum event() {
        return AfterSaleStateChangeEnum.REFUND_SUCCESS;
    }

    @Override
    protected AfterSaleStateMachineDTO onStateChangeInternal(AfterSaleStateChangeEnum afterSaleStateChangeEnum,
                                                             AfterSaleStateMachineDTO afterSaleStateMachineDTO) {
        PayRefundCallbackParam payRefundCallbackParam = afterSaleStateMachineDTO.getPayRefundCallbackParam();
        String afterSaleId = payRefundCallbackParam.getAfterSaleId();
        String key = RedisLockKeyConstants.REFUND_KEY + afterSaleId;
        boolean lock = redisLock.tryLock(key);
        if (!lock) {
            throw new OrderBizException(OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_REPEAT);
        }
        try {
            // 1、入参校验
            checkPayRefundCallbackParam(payRefundCallbackParam);
            // 2、支付回调更新售后信息
            updateAfterSaleData(payRefundCallbackParam,afterSaleStateChangeEnum);
            // 3、退款成功 发送通知
            if (RefundStatusEnum.REFUND_SUCCESS.getCode().equals(payRefundCallbackParam.getRefundStatus())) {
                // 发短信
                messageManager.sendRefundMobileMessage(afterSaleId);
                // 发APP通知
                messageManager.sendRefundAppMessage(afterSaleId);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new OrderBizException(OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_FAILED);
        } finally {
            redisLock.unlock(key);
        }
        return afterSaleStateMachineDTO;
    }

    /**
     * 支付回调更新售后信息
     * @author xuyou
     * 2023-02-26 22:07
    */
    private void updateAfterSaleData(PayRefundCallbackParam payRefundCallbackParam,
                                     AfterSaleStateChangeEnum afterSaleStateChangeEnum) {
        //  @Transactional无法生效，需要用编程式事务
        transactionTemplate.execute(transactionStatus -> {
            String afterSaleId = payRefundCallbackParam.getAfterSaleId();
            Integer refundStatus = payRefundCallbackParam.getRefundStatus();
            // 获取 afterSaleInfoDO
            AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);

            AfterSaleRefundDO afterSaleRefundDO = new AfterSaleRefundDO();
            afterSaleRefundDO.setAfterSaleId(payRefundCallbackParam.getAfterSaleId());
            afterSaleRefundDO.setRefundPayTime(payRefundCallbackParam.getRefundTime());

            AfterSaleLogDO afterSaleLogDO = null;
            Integer preStatus = AfterSaleStatusEnum.REFUNDING.getCode();

            // 退款状态 - 成功
            if (RefundStatusEnum.REFUND_SUCCESS.getCode().equals(refundStatus)) {
                // 封装 售后退款单 - 退款成功
                afterSaleRefundDO.setRefundStatus(RefundStatusEnum.REFUND_SUCCESS.getCode());
                afterSaleRefundDO.setRemark(RefundStatusEnum.REFUND_SUCCESS.getMsg());
                // 封装 售后单变更日志 - 退款成功
                afterSaleLogDO = afterSaleOperateLogFactory.buildAfterSaleLog(afterSaleId, preStatus,
                        AfterSaleStatusEnum.REFUNDED.getCode(),
                        AfterSaleOperateTypeEnum.AFTER_SALE_REFUNDED.getMsg());
                // 更新 订单售后表  退款中 -> 退款成功
                afterSaleInfoDAO.updateStatus(afterSaleId, AfterSaleStatusEnum.REFUNDING.getCode(),
                        AfterSaleStatusEnum.REFUNDED.getCode());
            } else {
                // 封装 售后退款单 - 退款失败
                afterSaleRefundDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getCode());
                afterSaleRefundDO.setRemark(RefundStatusEnum.REFUND_FAIL.getMsg());
                // 封装 售后单变更日志 - 退款失败
                afterSaleLogDO = afterSaleOperateLogFactory.buildAfterSaleLog(afterSaleId, preStatus,
                        AfterSaleStatusEnum.FAILED.getCode(),
                        AfterSaleOperateTypeEnum.AFTER_SALE_REFUND_FAIL.getMsg());
                // 更新 订单售后表  退款中 -> 退款失败
                afterSaleInfoDAO.updateStatus(afterSaleId, AfterSaleStatusEnum.REFUNDING.getCode(),
                        AfterSaleStatusEnum.FAILED.getCode());
            }

            // 新增 售后单变更表
            afterSaleLogDO.setOrderId(afterSaleInfoDO.getOrderId());
            afterSaleLogDAO.save(afterSaleLogDO);
            //  更新 售后退款单表
            afterSaleRefundDAO.updateAfterSaleRefundStatus(afterSaleRefundDO);
            return true;
        });
    }

    /**
     * 参数校验
     * @author xuyou
     * 2023-02-26 21:41
    */
    private void checkPayRefundCallbackParam(PayRefundCallbackParam payRefundCallbackParam) {
        ParamCheckUtil.checkObjectNonNull(payRefundCallbackParam);

        String orderId = payRefundCallbackParam.getOrderId();
        ParamCheckUtil.checkStringNonEmpty(orderId, OrderErrorCodeEnum.CANCEL_ORDER_ID_IS_NULL);

        String batchNo = payRefundCallbackParam.getBatchNo();
        ParamCheckUtil.checkStringNonEmpty(batchNo, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_BATCH_NO_IS_NULL);

        Integer refundStatus = payRefundCallbackParam.getRefundStatus();
        ParamCheckUtil.checkObjectNonNull(refundStatus, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_STATUS_NO_IS_NUL);

        Integer refundFee = payRefundCallbackParam.getRefundFee();
        ParamCheckUtil.checkObjectNonNull(refundFee, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_FEE_NO_IS_NUL);

        Integer totalFee = payRefundCallbackParam.getTotalFee();
        ParamCheckUtil.checkObjectNonNull(totalFee, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_TOTAL_FEE_NO_IS_NUL);

        String sign = payRefundCallbackParam.getSign();
        ParamCheckUtil.checkStringNonEmpty(sign, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_SIGN_NO_IS_NUL);

        String tradeNo = payRefundCallbackParam.getTradeNo();
        ParamCheckUtil.checkStringNonEmpty(tradeNo, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_TRADE_NO_IS_NUL);

        String afterSaleId = payRefundCallbackParam.getAfterSaleId();
        ParamCheckUtil.checkStringNonEmpty(afterSaleId, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_AFTER_SALE_ID_IS_NULL);

        Date refundTime = payRefundCallbackParam.getRefundTime();
        ParamCheckUtil.checkObjectNonNull(refundTime, OrderErrorCodeEnum.PROCESS_PAY_REFUND_CALLBACK_AFTER_SALE_REFUND_TIME_IS_NULL);
        // 未退款
        if (RefundStatusEnum.UN_REFUND.getCode().equals(payRefundCallbackParam.getRefundStatus())) {
            throw new OrderBizException(OrderErrorCodeEnum.PAY_REFUND_CALLBACK_STATUS_FAILED);
        }
        //  数据库中当前售后单不是未退款状态,表示已经退款成功 or 失败,那么本次就不能再执行支付回调退款
        AfterSaleRefundDO afterSaleByDatabase = afterSaleRefundDAO.getOneByAfterSaleId(afterSaleId);
        if (!RefundStatusEnum.UN_REFUND.getCode().equals(afterSaleByDatabase.getRefundStatus())) {
            throw new OrderBizException(OrderErrorCodeEnum.REPEAT_CALLBACK);
        }
    }

}
